import sys
import os
import ply.yacc as yacc

class RPC:
    __instance = None
    @staticmethod 
    def Instance():
        """ Static access method. """
        if RPC.__instance == None:
            RPC()
        return RPC.__instance

    def __init__(self):
        self.interfaces = {}
        self.curInterface = Interface()
        """ Virtually private constructor. """
        if RPC.__instance != None:
            raise Exception("This class is a singleton!")
        else:
            RPC.__instance = self
            
    def getInterfaces(self):
        return self.interfaces
            
    def getCurInterface(self):
        return self.curInterface
        
    def newInterface(self):
        self.interfaces[self.curInterface.getName()] = self.curInterface
        self.curInterface = Interface()
        return self.curInterface

class Method:
    def __init__(self, name, req, ack):
        self.name = name
        self.req = req
        self.ack = ack
        
    def getName(self):
        return self.name
        
    def getAck(self):
        return self.ack
        
    def getReq(self):
        return self.req
        
    def __str__(self):
        return 'Method name:' + self.name + '\n\tREQ:' + self.req + ' ACK:' + self.ack + '\n'
        
class Interface:
    def __init__(self):
        self.methods = []
        self.entityClass = None
        
    def setName(self, name):
        self.name = name
        
    def getName(self):
        return self.name
        
    def getMethods(self):
        return self.methods;
        
    def addMethod(self, method):
        self.methods.append(method)
        
    def setEntityClass(self, name):
        self.entityClass = name
        
    def getEntityClass(self):
        return self.entityClass
        
    def __str__(self):
        s = ''
        for method in self.methods:
            s += str(method)
        return 'Interface ' + self.name + '\n' + s
        
tokens = [
    'NAME','LPAREN','RPAREN','LROUND','RROUND','SIMECOLON'
]

reserved = {
    'rpc' : 'RPC'
}

tokens += reserved.values()

t_LPAREN = r'\{'
t_RPAREN = r'\}'
t_LROUND = r'\('
t_RROUND = r'\)'
t_SIMECOLON = r'\:'
t_ignore = '\t\r '

def t_NAME(t):
    r'[a-zA-Z_][a-zA-Z0-9_]*'
    if t.value in reserved:
        t.type = reserved[t.value]
    return t

def t_NEWLINE(t):
    r'\n+'
    t.lexer.lineno += t.value.count("\n")
    
def t_error(t):
    pass

import ply.lex as lex
lex.lex()
    
def p_rpc_file(p):
    'rpc_file : rpc_class_list'
    pass
    
def p_rpc_class_list(p):
    '''rpc_class_list : rpc_class_list rpc_class
                      | rpc_class'''
    pass
    
def p_rpc_class(p):
    'rpc_class : RPC NAME LPAREN method_list RPAREN'
    RPC.Instance().getCurInterface().setName(p[2])
    RPC.Instance().newInterface()
    
def p_rpc_class1(p):
    'rpc_class : RPC NAME LPAREN method_list RPAREN SIMECOLON NAME'
    RPC.Instance().getCurInterface().setName(p[2])
    RPC.Instance().getCurInterface().setEntityClass(p[6])
    RPC.Instance().newInterface()
    
def p_method_list(p):
    '''method_list : method_list method'''
    RPC.Instance().getCurInterface().addMethod(p[2])
    
def p_method_list1(p):
    '''method_list : method'''
    RPC.Instance().getCurInterface().addMethod(p[1])
    
def p_method(p):
    'method : NAME NAME LROUND NAME RROUND'
    p[0] = Method(p[2], p[4], p[1])
    
def p_error(p):
    pass
       
rpc_cpp_interface_decl_begin = '''\
////////////////////////////////////////////////
// MACHINE GENERATED, PLEASE DONOT MODIFY!    //
////////////////////////////////////////////////

#pragma once

#include <functional>
#include <memory>
#include <ctime>
#include <google/protobuf/message.h>

#include "../common/generated_proto_header.hh"
#include "../../plugin/base/rpc_define.hh"

#include "rpc_id.inl"

'''

rpc_cpp_rpc_generated_hh = '''\
////////////////////////////////////////////////
// MACHINE GENERATED, PLEASE DONOT MODIFY!    //
////////////////////////////////////////////////

#pragma once

#include "rpc_interface_generated.hh"

class RPCSkeleton : public RPCSkeletonBase {
public:
    static void install();
    static void uninstall();

public:
    RPCSkeleton();
    virtual ~RPCSkeleton();
    virtual void dispose(std::uint64_t RPCID, RPCCallBase* callBase) override;
    virtual void onPathReq(PathID pathID, std::uint64_t RPCID, std::uint64_t UUID,
        const char* data, std::size_t size) override;
    virtual void onPathAck(std::uint64_t RPCID, RPCCallBase* callBase, const char* data, std::size_t size) override;
    virtual void onSiteReq(const kratos::grid::SiteID & siteID, std::uint64_t RPCID, std::uint64_t UUID,
        const char * data, std::size_t size) override;
    virtual void onSiteAck(std::uint64_t RPCID, RPCCallBase* callBase, const char* data, std::size_t size) override;
    virtual void onEntityReq(PathID pathID, PathID fromPathID, std::uint64_t entityID, std::uint64_t RPCID, std::uint64_t UUID,
        const char* data, std::size_t size) override;
    virtual void onEntityAck(std::uint64_t RPCID, RPCCallBase* callBase, const char* data, std::size_t size) override;
'''

rpc_cpp_rpc_generated_hh_lua = '''\
////////////////////////////////////////////////
// MACHINE GENERATED, PLEASE DONOT MODIFY!    //
////////////////////////////////////////////////

#pragma once

#include "rpc_interface_generated.hh"

class RPCSkeleton : public RPCSkeletonBase {
public:
    static void install();
    static void uninstall();

public:
    RPCSkeleton();
    virtual ~RPCSkeleton();
    virtual void dispose(std::uint64_t RPCID, RPCCallBase* callBase) override;
    virtual void onPathReq(PathID pathID, std::uint64_t RPCID, std::uint64_t UUID,
        const char* data, std::size_t size) override;
    virtual void onPathAck(std::uint64_t RPCID, RPCCallBase* callBase, const char* data, std::size_t size) override;
        virtual void onSiteReq(const kratos::grid::SiteID & , std::uint64_t, std::uint64_t,
        const char *, std::size_t) override {}
    virtual void onSiteAck(std::uint64_t, RPCCallBase*, const char*, std::size_t) override {}
    virtual void onEntityReq(PathID, PathID, std::uint64_t, std::uint64_t, std::uint64_t,
        const char*, std::size_t) override;
    virtual void onEntityAck(std::uint64_t, RPCCallBase*, const char*, std::size_t) override;
'''

rpc_cpp_rpc_generated_cpp_begin = '''\
////////////////////////////////////////////////
// MACHINE GENERATED, PLEASE DONOT MODIFY!    //
////////////////////////////////////////////////

#pragma once

#include "../../plugin/base/module_frame.hh"
#include "../../plugin/base/framework.hh"
#include "../../plugin/protocol/protocol_header.hh"
#include "../../plugin/base/rpc_base.hh"
#include "../../plugin/base/entity.hh"
#include "../../corelib/object_pool.hpp"
#include "rpc_generated.hh"

enum class ProxyType {
    PROXYTYPE_PATH = 1,
    PROXYTYPE_SITE,
    PROXYTYPE_ENTITY,
};

RPCSkeleton* skeleton_ = nullptr;

void RPCSkeleton::install() {
    if (!skeleton_) {
        skeleton_ = new RPCSkeleton();
    }
}

void RPCSkeleton::uninstall() {
    delete skeleton_;
    skeleton_ = nullptr;
}

RPCSkeleton::RPCSkeleton() {
    RPCRef.registerSkeleton(this);
}

RPCSkeleton::~RPCSkeleton() {
    RPCRef.registerSkeleton(nullptr);
}

template <typename MSG, typename METHOD>
static bool callMethod(ProxyType type, std::uint64_t id, std::uint64_t RPCID, const MSG& msg, METHOD method, RPCTimeoutCB toCB, std::time_t timeout) {
    auto call = kratos::corelib::allocate<RPCCall<METHOD>>();
    if (!call) {
        return false;
    }
    call->ackMethod = method;
    if (type == ProxyType::PROXYTYPE_PATH) {
        if (!RPCRef.RPCCallByPath(static_cast<PathID>(id), RPCID, msg, call, toCB, timeout)) {
            kratos::corelib::dispose(call);
            return false;
        }
    } else if (type == ProxyType::PROXYTYPE_SITE) {
        if (!RPCRef.RPCCallBySite(kratos::grid::SiteID(id), RPCID, msg, call, toCB, timeout)) {
            kratos::corelib::dispose(call);
            return false;
        }
    } else if (type == ProxyType::PROXYTYPE_ENTITY) {
        if (!RPCRef.RPCCallByEntity(id, RPCID, msg, call, toCB, timeout)) {
            kratos::corelib::dispose(call);
            return false;
        }
    } else {
        kratos::corelib::dispose(call);
        return false;
    }
    return true;
}

'''

rpc_cpp_rpc_generated_cpp_begin_lua = '''\
////////////////////////////////////////////////
// MACHINE GENERATED, PLEASE DONOT MODIFY!    //
////////////////////////////////////////////////

#pragma once

#include "../../plugin/base/module_frame.hh"
#include "../../plugin/base/framework.hh"
#include "../../plugin/protocol/protocol_header.hh"
#include "../../plugin/base/rpc_base.hh"
#include "../../plugin/base/entity.hh"
#include "../../corelib/object_pool.hpp"
#include "rpc_interface_generated.hh"
#include "rpc_generated.hh"

enum class ProxyType {
    PROXYTYPE_PATH = 1,
    PROXYTYPE_SITE,
    PROXYTYPE_ENTITY,
};

RPCSkeleton* skeleton_ = nullptr;

void RPCSkeleton::install() {
    if (!skeleton_) {
        skeleton_ = new RPCSkeleton();
    }
}

void RPCSkeleton::uninstall() {
    delete skeleton_;
    skeleton_ = nullptr;
}

RPCSkeleton::RPCSkeleton() {
    RPCRef.registerSkeleton(this);
}

RPCSkeleton::~RPCSkeleton() {
    RPCRef.registerSkeleton(nullptr);
}

static bool callMethod(ProxyType type, std::uint64_t id, std::uint64_t RPCID, const google::protobuf::Message& msg, AckFunction method, RPCTimeoutCB toCB, std::time_t timeout) {
    auto call = kratos::corelib::allocate<RPCCall<AckFunction>>();
    if (!call) {
        return false;
    }
    call->ackMethod = method;
    if (type == ProxyType::PROXYTYPE_PATH) {
        if (!RPCRef.RPCCallByPath(static_cast<PathID>(id), RPCID, msg, call, toCB, timeout)) {
            kratos::corelib::dispose(call);
            return false;
        }
    } else if (type == ProxyType::PROXYTYPE_SITE) {
        if (!RPCRef.RPCCallBySite(kratos::grid::SiteID(id), RPCID, msg, call, toCB, timeout)) {
            kratos::corelib::dispose(call);
            return false;
        }
    } else if (type == ProxyType::PROXYTYPE_ENTITY) {
        if (!RPCRef.RPCCallByEntity(id, RPCID, msg, call, toCB, timeout)) {
            kratos::corelib::dispose(call);
            return false;
        }
    } else {
        kratos::corelib::dispose(call);
        return false;
    }
    return true;
}

'''

rpc_lua_bind_rpc_generated_hh_header = '''\
////////////////////////////////////////////////
// MACHINE GENERATED, PLEASE DONOT MODIFY!    //
////////////////////////////////////////////////

#pragma

#include "../../lua/user/thirdparty/lua-5.3.5/src/lua.hpp"
#include "../common/generated_proto_header.hh"

struct LuaRPC {
    static bool installRPCProxyLibrary();
    static bool uninstallRPCProxyLibrary();
'''

rpc_lua_bind_rpc_generated_cpp_header = '''\
////////////////////////////////////////////////
// MACHINE GENERATED, PLEASE DONOT MODIFY!    //
////////////////////////////////////////////////

#include "lua_bind_rpc_generated.hh"
#include "../../lua/user/lua_bind.hh"
#include "../../lua/user/lua_helper.hh"
#include "../../lua/user/lua_entity.hh"
#include "../../plugin/base/entity.hh"
#include "rpc_generated.hh"
#include <memory>

'''

rpc_lua_call_rpc_path_proxy_method = '''\
int callRPCPathI{cap_interface_name}{cap_method_name}(lua_State * L) {
    {req} req;
    copyTableLuaToC(L, req);
    lua_pop(L, 1);
    auto timeout = (std::uint64_t)lua_tointeger(L, -1);
    auto pathID = (std::uint64_t)lua_tointeger(L, -2);
    auto result = RPCSkeleton::Path::get{cap_interface_name}(pathID)->{method_name}(req,
        [L](const Message& ack) {
            onCallAck(L, ack);           
        },
        [L]() {
            onCallTimeout(L);
        },
        timeout
        );
    if (!result) {
        lua_pushnil(L);
        return 1;
    }
    return lua_yieldk(L, 0, 0, nullptr);
}

int callRPCEntityI{cap_interface_name}{cap_method_name}(lua_State * L) {
    {req} req;
    copyTableLuaToC(L, req);
    lua_pop(L, 1);
    auto timeout = (std::uint64_t)lua_tointeger(L, -1);
    auto entityID = (std::uint64_t)lua_tointeger(L, -2);
    auto result = RPCSkeleton::Entity::get{cap_interface_name}(entityID)->{method_name}(req,
        [L](const Message& ack) {
            onCallAck(L, ack);           
        },
        [L]() {
            onCallTimeout(L);
        },
        timeout
        );
    if (!result) {
        lua_pushnil(L);
        return 1;
    }
    return lua_yieldk(L, 0, 0, nullptr);
}

'''

rpc_lua_call_stub_method = '''\
bool LuaRPC::luaCallI{cap_interface_name}{cap_method_name}(std::uint64_t pathID, std::uint64_t fromPathID, std::uint64_t entityID, std::uint64_t uuid, std::uint64_t rpcID, const google::protobuf::Message& req, google::protobuf::Message& ack) {
    if (entityID) {
        auto entity = EntityManagerRef.getEntityByType<LuaEntity>(entityID);
        if (entity) {
            return entity->callLuaImplMethod(pathID, fromPathID, uuid, rpcID, "{interface_name}", "{method_name}", req, ack);
        }
    }
    return LuaRef.callLuaImplMethod("{interface_name}", "{method_name}", pathID, uuid, rpcID, req, ack);
}

'''

class CPPGenerator:
    def __init__(self, output, isLua = False):
        self.output = output
        self.isLua = isLua
        
    def generate_rpc_interface_generated_hh(self):
        file = open(os.path.join(self.output, 'rpc_interface_generated.hh'), 'w')
        file.write(rpc_cpp_interface_decl_begin)
        if self.isLua:
            file.write('using AckFunction=std::function<void(const Message&)>;\n')
        for interface in RPC.Instance().getInterfaces().values():
            for method in interface.getMethods():
                if not self.isLua:
                    file.write('using ' + interface.getName().capitalize() + method.getName().capitalize() + 'Function=std::function<void(const ' + method.getAck() + '&)>;\n')
        file.write('\n')
        for interface in RPC.Instance().getInterfaces().values():
            file.write('class I' + interface.getName().capitalize() + ' {\n')
            file.write('public:\n')
            for method in interface.getMethods():
                if self.isLua:
                    file.write('\tvirtual void ' + method.getName() + '(const Message&, Message&)=0;\n')
                else:
                    file.write('\tvirtual void ' + method.getName() + '(const ' + method.getReq() + '&, ' + method.getAck() + '&)=0;\n')
            file.write('};\n') 
            file.write('class ' + interface.getName().capitalize() + 'Proxy {\n')
            file.write('public:\n')
            for method in interface.getMethods():
                if self.isLua:
                    file.write('\tvirtual bool ' + method.getName() + '(const Message&, AckFunction ackCB = nullptr, RPCTimeoutCB toCB = nullptr, std::time_t timeout = 2000) = 0;\n')
                else:
                    file.write('\tvirtual bool ' + method.getName() + '(const ' + method.getReq() + '&, ' + interface.getName().capitalize() + method.getName().capitalize() + 'Function ackCB = nullptr, RPCTimeoutCB toCB = nullptr, std::time_t timeout = 2000) = 0;\n')
            file.write('};\n') 
        file.close()
        
    def generate_rpc_id_inl(self):
        file = open(os.path.join(self.output, 'rpc_id.inl'), 'w')
        file.write('////////////////////////////////////////////////\n')
        file.write('// MACHINE GENERATED, PLEASE DONOT MODIFY!    //\n')
        file.write('////////////////////////////////////////////////\n\n')
        file.write('enum {\n')
        i = 1
        for interface in RPC.Instance().getInterfaces().values():
            for method in interface.getMethods():
                file.write('\tRPC_ID_' + interface.getName().upper() + '_' + method.getName().upper() + '=' + str(i) + ',\n')
                i += 1
        file.write('};\n')
        file.close()
             
    def generate_rpc_generated_hh(self):
        file = open(os.path.join(self.output, 'rpc_generated.hh'), 'w')
        if self.isLua:
            file.write(rpc_cpp_rpc_generated_hh_lua)
        else:
            file.write(rpc_cpp_rpc_generated_hh)
        file.write('\n')
        file.write('\tstruct Path {\n')
        for interface in RPC.Instance().getInterfaces().values():
            file.write('\t\tstatic std::shared_ptr<' + interface.getName().capitalize() + 'Proxy> get' + interface.getName().capitalize() + '(PathID pathID);\n')
        file.write('\t};\n')
        if not self.isLua:
            file.write('\tstruct Site {\n')
            for interface in RPC.Instance().getInterfaces().values():
                file.write('\t\tstatic std::shared_ptr<' + interface.getName().capitalize() + 'Proxy> get' + interface.getName().capitalize() + '(const kratos::grid::SiteID& siteID);\n')
            file.write('\t};\n')   
            for interface in RPC.Instance().getInterfaces().values():
                file.write('\tstatic void registerImpl(I' + interface.getName().capitalize() + '* impl);\n')
        file.write('\tstruct Entity {\n')
        for interface in RPC.Instance().getInterfaces().values():
            file.write('\t\tstatic std::shared_ptr<' + interface.getName().capitalize() + 'Proxy> get' + interface.getName().capitalize() + '(std::uint64_t entityID);\n')
        file.write('\t};\n')
        file.write('};\n')
        file.close()
        
    def generate_rpc_generated_cpp(self):
        file = open(os.path.join(self.output, 'rpc_generated.cpp'), 'w')
        if self.isLua:
            file.write(rpc_cpp_rpc_generated_cpp_begin_lua)
        else:
            file.write(rpc_cpp_rpc_generated_cpp_begin)
        if self.isLua:
            file.write('#include "lua_bind_rpc_generated.hh"\n\n')
        for interface in RPC.Instance().getInterfaces().values():
            file.write('I' + interface.getName().capitalize() + '* ' + interface.getName() + '_ = nullptr;\n')
        file.write('\n')        
        for interface in RPC.Instance().getInterfaces().values():
            for method in interface.getMethods():
                if self.isLua:
                    file.write('static bool ON_' + interface.getName().upper() + '_' + method.getName().upper() + '(PathID pathID, std::uint64_t UUID, std::uint64_t rpcID, const Message& req, Message& ack, std::uint64_t entityID = 0, std::uint64_t fromPathID = 0);\n')
                else:
                    file.write('static void ON_' + interface.getName().upper() + '_' + method.getName().upper() + '(const ' + method.getReq() + '& req,' + method.getAck() + '& ack, std::uint64_t entityID = 0);\n')
                file.write('static void ON_PATH_' + interface.getName().upper() + '_' + method.getName().upper() + '(PathID pathID, std::uint64_t UUID, std::uint64_t RPCID, const char* data, std::size_t size, ProxyType proxyType = ProxyType::PROXYTYPE_PATH, PathID fromPathID = 0, std::uint64_t entityID = 0);\n')
                if not self.isLua:
                    file.write('static void ON_SITE_' + interface.getName().upper() + '_' + method.getName().upper() + '(const kratos::grid::SiteID& siteID, std::uint64_t UUID, std::uint64_t RPCID, const char* data, std::size_t size);\n')
                file.write('static void ON_' + interface.getName().upper() + '_' + method.getName().upper() + '_ACK(RPCCallBase* callBase, const char* data, std::size_t size);\n')
                file.write('\n')
            file.write('\n')
        if not self.isLua:
            for interface in RPC.Instance().getInterfaces().values():
                file.write('void RPCSkeleton::registerImpl(I' + interface.getName().capitalize() + '* impl) {\n')
                file.write('\t' + interface.getName() + '_ = impl;\n')
                file.write('}\n')
                file.write('\n')
        file.write('\n')
        for interface in RPC.Instance().getInterfaces().values():
            file.write('class RPCInterface_' + interface.getName().capitalize() + '_proxy_impl : public ' + interface.getName().capitalize() + 'Proxy {\n')
            file.write('\tProxyType proxyType_;\n')
            file.write('\tstd::uint64_t id_;\n\n')
            file.write('public:\n')
            file.write('\tRPCInterface_' + interface.getName().capitalize() + '_proxy_impl(ProxyType type, std::uint64_t id) {\n')
            file.write('\t\tproxyType_ = type;\n')
            file.write('\t\tid_ = id;\n')
            file.write('\t}\n')
            for method in interface.getMethods():
                if self.isLua:
                    file.write('\tvirtual bool ' + method.getName() + '(const Message& req, AckFunction method = nullptr, RPCTimeoutCB toCB = nullptr, std::time_t timeout = 2000) override {\n')
                else:
                    file.write('\tvirtual bool ' + method.getName() + '(const ' + method.getReq() + '& req, ' + interface.getName().capitalize() + method.getName().capitalize() + 'Function method = nullptr, RPCTimeoutCB toCB = nullptr, std::time_t timeout = 2000) override {\n')
                if self.isLua:
                    file.write('\t\treturn callMethod(proxyType_, id_, RPC_ID_' + interface.getName().upper() + '_' + method.getName().upper() + ',req, method, toCB, timeout);\n')
                else:
                    file.write('\t\treturn callMethod<' + method.getReq() + ',' + interface.getName().capitalize() + method.getName().capitalize() + 'Function>(proxyType_, id_, RPC_ID_' + interface.getName().upper() + '_' + method.getName().upper() + ',req, method, toCB, timeout);\n')
                file.write('\t}\n')
            file.write('};\n')
            file.write('\n')
        for interface in RPC.Instance().getInterfaces().values():
            file.write('std::shared_ptr<' + interface.getName().capitalize() + 'Proxy> RPCSkeleton::Path::get' + interface.getName().capitalize() + '(PathID pathID) {\n')
            file.write('\treturn std::shared_ptr<' + interface.getName().capitalize() + 'Proxy>(new RPCInterface_' + interface.getName() + '_proxy_impl(ProxyType::PROXYTYPE_PATH,static_cast<std::uint64_t>(pathID)));\n')
            file.write('}\n')
            file.write('\n')
            if not self.isLua:
                file.write('std::shared_ptr<' + interface.getName().capitalize() + 'Proxy> RPCSkeleton::Site::get' + interface.getName().capitalize() + '(const kratos::grid::SiteID& siteID) {\n')
                file.write('\treturn std::shared_ptr<' + interface.getName().capitalize() + 'Proxy>(new RPCInterface_' + interface.getName() + '_proxy_impl(ProxyType::PROXYTYPE_SITE,siteID.u64()));\n')
                file.write('}\n')
                file.write('\n')
            file.write('std::shared_ptr<' + interface.getName().capitalize() + 'Proxy> RPCSkeleton::Entity::get' + interface.getName().capitalize() + '(std::uint64_t entityID) {\n')
            file.write('\treturn std::shared_ptr<' + interface.getName().capitalize() + 'Proxy>(new RPCInterface_' + interface.getName() + '_proxy_impl(ProxyType::PROXYTYPE_ENTITY,entityID));\n')
            file.write('}\n')
            file.write('\n')
        file.write('void RPCSkeleton::dispose(std::uint64_t RPCID, RPCCallBase * callBase) {\n')
        if self.isLua:
            file.write('\tkratos::corelib::dispose(dynamic_cast<RPCCall<AckFunction>*>(callBase));\n')
        else:
            file.write('\tswitch (RPCID) {\n')
            for interface in RPC.Instance().getInterfaces().values():
                for method in interface.getMethods():
                    file.write('\t\tcase RPC_ID_' + interface.getName().upper() + '_' + method.getName().upper() + ':\n')
                    file.write('\t\t\tkratos::corelib::dispose(dynamic_cast<RPCCall<' + interface.getName().capitalize() + method.getName().capitalize() + 'Function>*>(callBase));\n')
                    file.write('\t\t\treturn;\n')
                file.write('\t\tdefault:\n')
                file.write('\t\t\treturn;\n')
                file.write('\t}\n')
        file.write('}\n\n')
        
        file.write('void RPCSkeleton::onPathReq(PathID pathID, std::uint64_t RPCID, std::uint64_t UUID, const char * data, std::size_t size) {\n')
        file.write('\tswitch (RPCID) {\n')
        for interface in RPC.Instance().getInterfaces().values():
            for method in interface.getMethods():
                file.write('\t\tcase RPC_ID_' + interface.getName().upper() + '_' + method.getName().upper() + ':\n')
                file.write('\t\t\tON_PATH_' + interface.getName().upper() + '_' + method.getName().upper() + '(pathID, UUID, RPCID, data, size);\n')
                file.write('\t\t\tbreak;\n')
        file.write('\t\tdefault:\n')
        file.write('\t\t\treturn;\n')
        file.write('\t}\n')
        file.write('}\n')
        
        file.write('void RPCSkeleton::onPathAck(std::uint64_t RPCID, RPCCallBase * callBase, const char * data, std::size_t size) {\n')
        file.write('\tswitch (RPCID) {\n')
        for interface in RPC.Instance().getInterfaces().values():
            for method in interface.getMethods():
                file.write('\t\tcase RPC_ID_' + interface.getName().upper() + '_' + method.getName().upper() + ':\n')
                file.write('\t\t\tON_' + interface.getName().upper() + '_' + method.getName().upper() + '_ACK(callBase, data, size);\n')
                file.write('\t\t\tbreak;\n')
        file.write('\t\tdefault:\n')
        file.write('\t\t\treturn;\n')
        file.write('\t}\n')
        file.write('}\n')
        
        if not self.isLua:
            file.write('void RPCSkeleton::onSiteReq(const kratos::grid::SiteID & siteID, std::uint64_t RPCID, std::uint64_t UUID, const char * data, std::size_t size) {\n')
            file.write('\tswitch (RPCID) {\n')
            for interface in RPC.Instance().getInterfaces().values():
                for method in interface.getMethods():
                    file.write('\t\tcase RPC_ID_' + interface.getName().upper() + '_' + method.getName().upper() + ':\n')
                    file.write('\t\t\tON_SITE_' + interface.getName().upper() + '_' + method.getName().upper() + '(siteID, UUID, RPCID, data, size);\n')
                    file.write('\t\t\tbreak;\n')
            file.write('\t\tdefault:\n')
            file.write('\t\t\treturn;\n')
            file.write('\t}\n')
            file.write('}\n')
        if not self.isLua:
            file.write('void RPCSkeleton::onSiteAck(std::uint64_t RPCID, RPCCallBase * callBase, const char * data, std::size_t size) {\n')
            file.write('\tswitch (RPCID) {\n')
            for interface in RPC.Instance().getInterfaces().values():
                for method in interface.getMethods():
                    file.write('\t\tcase RPC_ID_' + interface.getName().upper() + '_' + method.getName().upper() + ':\n')
                    file.write('\t\t\tON_' + interface.getName().upper() + '_' + method.getName().upper() + '_ACK(callBase, data, size);\n')
                    file.write('\t\t\tbreak;\n')
            file.write('\t\tdefault:\n')
            file.write('\t\t\treturn;\n')
            file.write('\t}\n')
            file.write('}\n')
        file.write('void RPCSkeleton::onEntityReq(PathID pathID, PathID fromPathID, std::uint64_t entityID, std::uint64_t RPCID, std::uint64_t UUID, const char * data, std::size_t size) {\n')
        file.write('\tswitch (RPCID) {\n')
        for interface in RPC.Instance().getInterfaces().values():
            for method in interface.getMethods():
                file.write('\t\tcase RPC_ID_' + interface.getName().upper() + '_' + method.getName().upper() + ':\n')
                file.write('\t\t\tON_PATH_' + interface.getName().upper() + '_' + method.getName().upper() + '(pathID, UUID, RPCID, data, size, ProxyType::PROXYTYPE_ENTITY, fromPathID, entityID);\n')
                file.write('\t\t\tbreak;\n')
        file.write('\t\tdefault:\n')
        file.write('\t\t\treturn;\n')
        file.write('\t}\n')
        file.write('}\n\n')
        file.write('void RPCSkeleton::onEntityAck(std::uint64_t RPCID, RPCCallBase * callBase, const char * data, std::size_t size) {\n')
        file.write('\tonPathAck(RPCID, callBase, data, size);\n')
        file.write('}\n\n')
        
        for interface in RPC.Instance().getInterfaces().values():
            for method in interface.getMethods():
                file.write('void ON_PATH_' + interface.getName().upper() + '_' + method.getName().upper() + '(PathID pathID, std::uint64_t UUID, std::uint64_t RPCID, const char * data, std::size_t size, ProxyType proxyType, PathID fromPathID, std::uint64_t entityID) {\n')
                if self.isLua:
                    file.write('\tauto reqPtr = RPCRef.newPBProtoMessage("' + method.getReq() + '");\n')
                    file.write('\tif (!reqPtr) {\n')
                    file.write('\t\terrorlog << "Message not found \'' + method.getReq() + '\'" << endlog;\n');
                    file.write('\t\treturn;\n')
                    file.write('\t}\n')
                    file.write('\treqPtr->ParseFromArray(data, size);\n')
                    file.write('\tauto& req = *reqPtr;\n')
                else:
                    file.write('\t' + method.getReq() + ' req;\n')
                    file.write('\treq.ParseFromArray(data, size);\n')
                if self.isLua:
                    file.write('\tauto ackPtr = RPCRef.newPBProtoMessage("' + method.getAck() + '");\n')
                    file.write('\tif (!ackPtr) {\n')
                    file.write('\t\tRPCRef.deletePBProtoMessage(reqPtr);\n')
                    file.write('\t\terrorlog << "Message not found \'' + method.getAck() + '\'" << endlog;\n');
                    file.write('\t\treturn;\n')
                    file.write('\t}\n')
                    file.write('\tauto& ack = *ackPtr;\n')
                else:
                    file.write('\t' + method.getAck() + ' ack;\n')
                if self.isLua:
                    file.write('\tif (!ON_' + interface.getName().upper() + '_' + method.getName().upper() + '(pathID, UUID, RPCID, req, ack, entityID)) {\n')
                    file.write('\t\tRPCRef.deletePBProtoMessage(reqPtr);\n')
                    file.write('\t\treturn;\n')
                    file.write('\t}\n')
                else:
                    file.write('\tON_' + interface.getName().upper() + '_' + method.getName().upper() + '(req, ack, entityID);\n')
                file.write('\tif(proxyType==ProxyType::PROXYTYPE_PATH) {\n')
                file.write('\t\tsendPathAck(pathID, UUID, RPCID, ack);\n')
                file.write('\t} else {\n')
                file.write('\t\tsendEntityAck(pathID, fromPathID, UUID, RPCID, ack);\n')
                file.write('\t}\n')
                if self.isLua:
                    file.write('\tRPCRef.deletePBProtoMessage(reqPtr);\n')
                    file.write('\tRPCRef.deletePBProtoMessage(ackPtr);\n')
                file.write('}\n')
                file.write('\n')
                file.write('\n')
                
        for interface in RPC.Instance().getInterfaces().values():
            for method in interface.getMethods():
                if self.isLua:
                    file.write('bool ON_' + interface.getName().upper() + '_' + method.getName().upper() + '(PathID pathID, std::uint64_t uuid, std::uint64_t rpcID, const Message& req, Message& ack, std::uint64_t entityID, std::uint64_t fromPathID) {\n')
                else:
                    file.write('void ON_' + interface.getName().upper() + '_' + method.getName().upper() + '(const ' + method.getReq() + '& req, ' + method.getAck() + '& ack, std::uint64_t entityID) {\n')
                if self.isLua:
                    file.write('\treturn LuaRPC::luaCallI' + interface.getName().capitalize() + method.getName().capitalize() + '(pathID, fromPathID, entityID, uuid, rpcID, req, ack);\n')
                else:
                    file.write('\tif(entityID) {\n')
                    file.write('\t\tauto entity = EntityManagerRef.getEntityByType<I' + interface.getName().capitalize() + '>(entityID);\n')
                    file.write('\t\tif(entity) {\n')
                    file.write('\t\t\tentity->' + method.getName() + '(req, ack);\n')
                    file.write('\t\t} else {\n')
                    file.write('\t\t\tif (' + interface.getName() + '_) {\n')
                    file.write('\t\t\t\t' + interface.getName() + '_->' + method.getName() + '(req, ack);\n')
                    file.write('\t\t\t} else {\n')
                    file.write('\t\t\t\terrorlog << "Calling umimplemented interface method RPCInterface_' + interface.getName() + '::' + method.getName() + '" << endlog;\n')
                    file.write('\t\t\t}\n')
                    file.write('\t\t}\n')
                    file.write('\t\treturn;\n')
                    file.write('\t}\n')
                    file.write('\tif (' + interface.getName() + '_) {\n')
                    file.write('\t\t' + interface.getName() + '_->' + method.getName() + '(req, ack);\n')
                    file.write('\t} else {\n')
                    file.write('\t\terrorlog << "Calling umimplemented interface method RPCInterface_' + interface.getName() + '::' + method.getName() + '" << endlog;\n')
                    file.write('\t}\n')
                file.write('}\n')
                file.write('\n')
                
        if not self.isLua:
            for interface in RPC.Instance().getInterfaces().values():
                for method in interface.getMethods():
                    file.write('void ON_SITE_' + interface.getName().upper() + '_' + method.getName().upper() + '(const kratos::grid::SiteID & siteID, std::uint64_t UUID, std::uint64_t RPCID, const char * data, std::size_t size) {\n')
                    file.write('\t' + method.getReq() + ' req;\n')
                    file.write('\treq.ParseFromArray(data, size);\n')
                    file.write('\t' + method.getAck() + ' ack;\n')
                    file.write('\tON_' + interface.getName().upper() + '_' + method.getName().upper() + '(req, ack);\n')
                    file.write('\tsendSiteAck(siteID, UUID, RPCID, ack);\n')
                    file.write('}\n')
                    file.write('\n')
                
        for interface in RPC.Instance().getInterfaces().values():
            for method in interface.getMethods():
                file.write('void ON_' + interface.getName().upper() + '_' + method.getName().upper() + '_ACK(RPCCallBase * callBase, const char * data, std::size_t size) {\n')
                if self.isLua:
                    file.write('\tauto call = dynamic_cast<RPCCall<AckFunction>*>(callBase);\n')
                else:
                    file.write('\tauto call = dynamic_cast<RPCCall<' + interface.getName().capitalize() + method.getName().capitalize() + 'Function>*>(callBase);\n')
                file.write('\tif (!call->ackMethod) {\n')
                file.write('\t\treturn;\n')
                file.write('\t}\n')
                if self.isLua:
                    file.write('\tauto ackPtr = RPCRef.newPBProtoMessage("' + method.getAck() + '");\n')
                    file.write('\tif (!ackPtr) {\n')
                    file.write('\t\terrorlog << "Message not found \'' + method.getAck() + '\'" << endlog;\n');
                    file.write('\t\treturn;\n')
                    file.write('\t}\n')
                    file.write('\tauto& ack = *ackPtr;\n')
                else:
                    file.write('\t' + method.getAck() + ' ack;\n')
                file.write('\tack.ParseFromArray(data, size);\n')
                file.write('\ttry {\n')
                file.write('\t\tcall->ackMethod(ack);\n')
                file.write('\t} catch (std::exception& e) {\n')
                file.write('\t\terrorlog << "Exception:" << e.what() << ", when processing result of RPCID:" << callBase->RPCID << endlog;\n')
                file.write('\t}\n')
                if self.isLua:
                    file.write('\tRPCRef.deletePBProtoMessage(ackPtr);\n')
                file.write('}\n')
                file.write('\n')                
        file.close()
        
    def generate_lua_rpc_hh(self):
        file = open(os.path.join(self.output, 'lua_bind_rpc_generated.hh'), 'w')
        file.write(rpc_lua_bind_rpc_generated_hh_header);
        for interface in RPC.Instance().getInterfaces().values():
            for method in interface.getMethods():
                file.write('\tstatic bool luaCallI' + interface.getName().capitalize() + method.getName().capitalize() + '(std::uint64_t pathID, std::uint64_t fromPathID, std::uint64_t entityID, std::uint64_t uuid, std::uint64_t rpcID, const google::protobuf::Message& req, google::protobuf::Message& ack);\n')
        file.write('};\n\n')
        file.close()
        
    def generate_lua_rpc_cpp(self):
        file = open(os.path.join(self.output, 'lua_bind_rpc_generated.cpp'), 'w')
        file.write(rpc_lua_bind_rpc_generated_cpp_header)
        for interface in RPC.Instance().getInterfaces().values():
            for method in interface.getMethods():
                file.write(rpc_lua_call_rpc_path_proxy_method.replace('{cap_interface_name}', interface.getName().capitalize()).replace('{cap_method_name}', method.getName().capitalize()).replace('{req}', method.getReq()).replace('{ack}', method.getAck()).replace('{method_name}', method.getName()))
                file.write(rpc_lua_call_stub_method.replace('{cap_interface_name}', interface.getName().capitalize()).replace('{cap_method_name}', method.getName().capitalize()).replace('{interface_name}', interface.getName()).replace('{method_name}', method.getName()))
        file.write('bool LuaRPC::installRPCProxyLibrary() {\n')
        file.write('\tauto luaState = LuaRef.getVirtualMachine();\n')
        for interface in RPC.Instance().getInterfaces().values():
            for method in interface.getMethods():
                file.write('\tif (!installRPCMethod(luaState, \"RPC.Path.' + interface.getName() + '.' + method.getName() + '\", &callRPCPathI' + interface.getName().capitalize() + method.getName().capitalize() +')) {\n')
                file.write('\t\terrorlog << "Install RPC method \'RPC.Path.' + interface.getName() + '.' + method.getName() + '\' failed" << endlog;\n')
                file.write('\t\treturn false;\n')
                file.write('\t}\n')
                file.write('\tif (!installRPCMethod(luaState, \"RPC.Entity.' + interface.getName() + '.' + method.getName() + '\", &callRPCEntityI' + interface.getName().capitalize() + method.getName().capitalize() +')) {\n')
                file.write('\t\terrorlog << "Install RPC method \'RPC.Entity.' + interface.getName() + '.' + method.getName() + '\' failed" << endlog;\n')
                file.write('\t\treturn false;\n')
                file.write('\t}\n')
        file.write('\treturn true;\n')
        file.write('}\n\n')
        file.write('bool LuaRPC::uninstallRPCProxyLibrary() {\n')
        file.write('\tuninstallRPCMethod(LuaRef.getVirtualMachine(), \"RPC\");\n')
        file.write('\treturn true;\n')
        file.write('}\n')
        file.close()
        
    def generate_rpc_id_cfg(self):
        file = open(os.path.join(self.output, 'rpc_id.cfg'), 'w')
        i = 1
        for interface in RPC.Instance().getInterfaces().values():
            for method in interface.getMethods():
                file.write(str(i) + ' ' +  method.getReq() + ' ' + method.getAck() + '\n')
                i += 1
        file.close()

    def generate(self):
        self.generate_rpc_interface_generated_hh()
        self.generate_rpc_id_inl()
        self.generate_rpc_generated_hh()
        self.generate_rpc_generated_cpp()
        self.generate_rpc_id_cfg()
        if self.isLua:
            self.generate_lua_rpc_hh()
            self.generate_lua_rpc_cpp()
             
if __name__ == '__main__':
    genLua = False
    if len(sys.argv) == 3:
        if sys.argv[2] == '--lua':
            genLua = True
    parser = yacc.yacc()
    file = open(sys.argv[1])
    result = parser.parse(file.read())
    if genLua:
        cpp = CPPGenerator('../lua_rpc', isLua = genLua)
        cpp.generate()
    else:
        cpp = CPPGenerator('.', isLua = genLua)
        cpp.generate()